package com.gitee.qdbp.general.common.biz.sms.service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.result.ResultCode;
import com.gitee.qdbp.general.common.api.sms.model.ISmsTemplate;
import com.gitee.qdbp.general.common.error.GeneralErrorCode;
import com.gitee.qdbp.tools.files.PathTools;
import com.gitee.qdbp.tools.http.RemoteServiceException;
import com.gitee.qdbp.tools.utils.StringTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 吉信通短信接口<br>
 * http://www.winic.org/api/SendMessage.asp<br>
 * 这个平台是直接发送短信内容的, 不需要到平台配置短信模板
 *
 * @author zhaohuihua
 * @version 170628
 */
public class WinicSmsService extends BaseSmsService {

    private static final Logger log = LoggerFactory.getLogger(WinicSmsService.class);

    // 000/Send:1/Consumption:.07/Tmoney:833.82/sid:0628191022928257
    // -03/Send:1/Consumption:0/Tmoney:0/sid:
    private static final Pattern RESULT_PARSER = Pattern.compile("^\\s*([\\+\\-]?\\d+)\\s*/.+/\\s*sid\\:(\\w*)");

    public WinicSmsService() {
        this.properties = "settings/sms/sms.winic.txt";
    }

    @Override
    @PostConstruct
    public void init() {
        super.init();
    }

    @Override
    public String send(String phone, ISmsTemplate template, Object... args) throws ServiceException {

        utils.check(phone, template.name());

        String msg = "Failed to send Short Message. phone:{}, template:{}.";

        String[] placeholder = config.getArray("sms.content." + template + ".params", false);
        if (placeholder == null) {
            placeholder = new String[0];
        }
        if (args.length < placeholder.length) {
            log.error(msg + " The number of parameters is not enough.");
            throw new ServiceException(GeneralErrorCode.SMS_SEND_ERROR);
        }

        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < placeholder.length && i < args.length; i++) {
            map.put(placeholder[i], String.valueOf(args[i]));
        }

        String url = config.getString("sms.service.url");
        String account = config.getString("auth.account");
        String password = config.getString("auth.password");

        Charset charset = getCharset("encode.charset");

        String signature = config.getString("sms.signature", false);

        String content = config.getString("sms.content." + template);
        if (VerifyTools.isBlank(content)) {
            log.error(msg + " sms template not found.", phone, template);
            throw new ServiceException(GeneralErrorCode.SMS_SEND_ERROR);
        }

        String formatted = (signature == null ? "" : signature) + StringTools.format(content, map);

        StringBuilder p = new StringBuilder();
        p.append("?id=").append(encode(account, charset));
        p.append("&pwd=").append(password);
        p.append("&to=").append(phone);
        p.append("&time=").append(""); // 为空及时发送
        p.append("&content=").append(encode(formatted, charset));

        // 000/Send:1/Consumption:.07/Tmoney:833.82/sid:0628191022928257
        String result;
        try {
            result = PathTools.downloadString(url + p.toString()).trim();
        } catch (IOException e) {
            log.error(msg + "\n\t{}\n\t{}\n\t{}", phone, template, url, p, formatted, e);
            throw new ServiceException(GeneralErrorCode.SMS_SEND_ERROR);
        }

        Matcher matcher = RESULT_PARSER.matcher(result);
        if (!matcher.find()) {
            log.error(msg + " result format error:[{}]\n\t{}\n\t{}\n\t{}", phone, template, result, url, p, formatted);
            throw new ServiceException(GeneralErrorCode.SMS_SEND_ERROR);
        } else {
            String code = matcher.group(1);
            String sid = matcher.group(2);
            if (config.getString("resultcode.success").equals(code)) {
                String txt = "Send Short Message success. phone:{}, template:{}.";
                log.trace(txt + " result[{}]\n\t{}", phone, template, result, formatted);
                return sid;
            } else {
                String desc = config.getString("resultcode." + code, false);
                log.error(msg + " result:{}[{}]\n\t{}\n\t{}\n\t{}", phone, template, desc, result, url, p, formatted);
                if (VerifyTools.isBlank(desc)) {
                    desc = "UnknownException";
                }
                throw new ServiceException(GeneralErrorCode.SMS_SEND_ERROR, new RemoteServiceException(result, desc));
            }
        }

    }

    @Override
    public Double balances() throws ServiceException {
        throw new ServiceException(ResultCode.UNSUPPORTED_OPERATION);
    }

    private String encode(String text, Charset charset) throws ServiceException {
        try {
            return URLEncoder.encode(text, charset.name());
        } catch (UnsupportedEncodingException e) {
            log.error("Unsupported encoding charset [{}]", charset.name());
            throw new ServiceException(ResultCode.SERVER_INNER_ERROR);
        }
    }

    private Charset getCharset(String key) throws ServiceException {
        String charset = config.getString(key);
        try {
            return Charset.forName(charset);
        } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
            log.error("Unsupported encoding charset [{}], config key is [{}]", charset, key);
            throw new ServiceException(ResultCode.SERVER_INNER_ERROR);
        }
    }

}
